#ifndef __GLES2RenderSystem_H__
#define __GLES2RenderSystem_H__

#include "MaterialManager.h"
#include "RenderSystem.h"
#include "GLES2GpuProgram.h"

namespace PVM
{
	class GLES2Context;
	class GLES2Support;
	class GLES2RTTManager;
	class GLES2GpuProgramManager;
	class GLSLESProgramFactory;
	class GLSLESGpuProgram;
	class HardwareBufferManager;
	
	class GLES2RenderSystem : public RenderSystem
	{
	private:
		typedef HashMap<GLenum, GLuint> BindBufferMap;
		
		Matrix4 mViewMatrix;
		Matrix4 mWorldMatrix;
		Matrix4 mTextureMatrix;
		
		FilterOptions mMinFilter;
		FilterOptions mMipFilter;
		
		size_t mTextureMipmapCount;
		
		size_t mTextureCoordIndex[MAX_TEXTURE_LAYERS];
		GLenum mTextureTypes[MAX_TEXTURE_LAYERS];
		unsigned short mFixedFunctionTextureUnits;
		bool mColourWrite[4];
		bool mDepthWrite;
		uint32 mStencilMask;
		GLfloat mAutoTextureMatrix[16];
		bool mUseAutoTextureMatrix;
		GLES2Support* mGLSupport;
		GLES2Context* mMainContext;
		GLES2Context* mCurrentContext;
		GLES2GpuProgramManager* mGpuProgramManager;
		GLSLESProgramFactory* mGLSLESProgramFactory;
		HardwareBufferManager* mHardwareBufferManager;
		GLES2RTTManager* mRTTManager;
		GLenum mActiveTextureUnit;
		BindBufferMap mActiveBufferMap;
		bool mGLInitialised;
		GLenum mPolygonMode;
		GLint getCombineMinMipFilter(void) const;
		
		GLES2GpuProgram* mCurrentVertexProgram;
		GLES2GpuProgram* mCurrentFragmentProgram;
		
		GLint getTextureAddressingMode(TextureUnitState::TextureAddressingMode tam) const;
		GLenum getBlendMode(SceneBlendFactor Blend) const;
		
		bool activateGLTextureUnit(size_t unit);
		
	public:
		GLES2RenderSystem();
		virtual ~GLES2RenderSystem();
		
		friend class ShaderGeneratorTechniqueResolverListener;
		
		const std::string& getName(void) const;
		ConfigOptionMap& getConfigOptions(void);
		void setConfigOption(const std::string& name, const std::string& value);
		std::string validateConfigOptions(void);
		virtual RenderSystemCapabilities* createRenderSystemCapabilities() const;
		void initialiseFromRenderSystemCapabilities(RenderSystemCapabilities* caps, RenderTarget* primary);
		void reinitialise(void);
		void shutdown(void);
		void setAmbientLight(float r, float g, float b) {};
		void setShadingType(ShadeOptions so) {};
		void setLightingEnabled(bool enabled) {};
		DepthBuffer* _createDepthBufferFor(RenderTarget* renderTarget);
		void _getDepthStencilFormatFor(GLenum internalColourFormat, GLenum* depthFormat,
									   GLenum* stencilFormat);
		virtual MultiRenderTarget* createMultiRenderTarget(const std::string& name);
		std::string getErrorDescription(long errorNumber) const;
		VertexElementType getColourVertexElementType(void) const;
		void setNormaliseNormals(bool normalise) {};
		void _useLights(const LightList& lights, unsigned short limit) {};
		bool areFixedFunctionLightsInViewSpace() const {return true;}
		void _setWorldMatrix(const Matrix4& m);
		void _setViewMatrix(const Matrix4& m);
		void _setProjectionMatrix(const Matrix4& m);
		void _setSurfaceParams(const ColourValue& ambient,
							   const ColourValue& diffuse, const ColourValue& specular,
							   const ColourValue& emissive, Real shininess,
							   TrackVertexColourType tracking) {}
		void _setPointParameters(Real size, bool attenuationEnabled,
								 Real constant, Real linear, Real quadratic, Real minSize, Real maxSize) {}
		void _setPointSpritesEnabled(bool enabled) {}
		void _setTexture(size_t unit, bool enabled, const TexturePtr& tex);
		void _setTextureCoordSet(size_t stage, size_t index);
		void _setTextureCoordCalculation(size_t stage, TexCoordCalcMethod m,
										 const Frustum* frustum = 0) {};
		void _setTextureBlendMode(size_t stage, const LayerBlendModeEx& bm) {};
		void _setTextureAddressingMode(size_t stage, const TextureUnitState::UVWAddressingMode& uvw);
		void _setTextureBorderColour(size_t stage, const ColourValue& colour) {};
		void _setTextureMipmapBias(size_t unit, float bias) {}
		void _setTextureMatrix(size_t stage, const Matrix4& xform) {};
		void _setViewport(Viewport* vp);
		void _beginFrame(void);
		void _endFrame(void);
		void _setCullingMode(CullingMode mode);
		void _setDepthBufferParams(bool depthTest = true, bool depthWrite = true, CompareFunction depthFunction = CMPF_LESS_EQUAL);
		void _setDepthBufferCheckEnabled(bool enabled = true);
		void _setDepthBufferWriteEnabled(bool enabled = true);
		void _setDepthBufferFunction(CompareFunction func = CMPF_LESS_EQUAL);
		void _setDepthBias(float constantBias, float slopeScaleBias);
		void _setColourBufferWriteEnabled(bool red, bool green, bool blue, bool alpha);
		void _setFog(FogMode mode, const ColourValue& colour, Real density, Real start, Real end);
		void _convertProjectionMatrix(const Matrix4& matrix,
									  Matrix4& dest, bool forGpuProgram = false);
		void _makeProjectionMatrix(const Radian& fovy, Real aspect, Real nearPlane, Real farPlane,
								   Matrix4& dest, bool forGpuProgram = false);
		void _makeProjectionMatrix(Real left, Real right, Real bottom, Real top,
								   Real nearPlane, Real farPlane, Matrix4& dest, bool forGpuProgram = false);
		void _makeOrthoMatrix(const Radian& fovy, Real aspect, Real nearPlane, Real farPlane,
							  Matrix4& dest, bool forGpuProgram = false);
		void _applyObliqueDepthProjection(Matrix4& matrix, const Plane& plane,
										  bool forGpuProgram);
		void setClipPlane(ushort index, Real A, Real B, Real C, Real D);
		void enableClipPlane(ushort index, bool enable);
		void _setPolygonMode(PolygonMode level);
		void _setStencilCheckEnabled(bool enabled);
		void setStencilBufferParams(CompareFunction func = CMPF_ALWAYS_PASS,
									uint32 refValue = 0, uint32 mask = 0xFFFFFFFF,
									StencilOperation stencilFailOp = SOP_KEEP,
									StencilOperation depthFailOp = SOP_KEEP,
									StencilOperation passOp = SOP_KEEP,
									bool twoSidedOperation = false);
		void _setTextureUnitFiltering(size_t unit, FilterType ftype, FilterOptions filter);
		void _setTextureLayerAnisotropy(size_t unit, unsigned int maxAnisotropy);
		void setVertexDeclaration(VertexDeclaration* decl) {}
		void setVertexBufferBinding(VertexBufferBinding* binding) {}
		void _render(const RenderOperation& op);
		void setScissorTest(bool enabled, size_t left = 0, size_t top = 0, size_t right = 800, size_t bottom = 600);
		void clearFrameBuffer(unsigned int buffers,
							  const ColourValue& colour = ColourValue::Black,
							  Real depth = 1.0f, unsigned short stencil = 0);
		HardwareOcclusionQuery* createHardwareOcclusionQuery(void) {return NULL;}
		Real getHorzontalTexelOffset(void) {return 0.0;}
		Real getVerticalTexelOffset(void) {return 0.0;}
		Real getMinimumDepthInputValue(void) {return -1.0f;}
		Real getMaximumDepthInputValue(void) {return 1.0f;}
		void registerThread() {}
		void unregisterThread() {}
		void preExtraThreadsStarted() {}
		void postExtraThreadsStarted() {}
		void setClipPlanesImpl(const Ogre::PlaneList& planeList) {}
		
		GLES2Context* _getMainContext() {return mMainContext;}
		void _unregisterContext(GLES2Context* context);
		void _switchContext(GLES2Context* context);
		void _oneTimeContextInitialization();
		void initialiseContext(RenderWindow* primary);
		
		void _setRenderTarget(RenderTarget* target);
		
		GLint convertCompareFunction(CompareFunction func) const;
		GLint convertStencilOp(StencilOperation op, bool invert = false) const;
		
		void bindGpuProgram(GpuProgram* prg);
		void unbindGpuProgram(GpuProgramType gptype);
		void bindGpuProgramParameters(GpuProgramType gptype, GpuProgramParametersSharedPtr params, uint16 mask);
		void bindGpuProgramPassIterationParameters(GpuProgramType gptype);
		
		void _setSceneBlending(SceneBlendFactor sourceFactor, SceneBlendFactor destFactor, SceneBlendOperation op);
		void _setSeparateSceneBlending(SceneBlendFactor sourceFactor, SceneBlendFactor destFactor, SceneBlendFactor sourceFactorAlpha, SceneBlendFactor destFactorAlpha, SceneBlendOperation op, SceneBlendOperation alphaOp);
		void _setAlphaRejectSettings(CompareFunction func, unsigned char value, bool alphaToCoverage);
		unsigned int getDisplayMonitorCount() const;
		GLfloat _getCurrentAnisotropy(size_t unit);
		GLenum _getPolygonMode(void) {return mPolygonMode;}
		
		void _setSceneBlendingOperation(SceneBlendOperation op);
		void _setSeparateSceneBlendingOperation(SceneBlendOperation op, SceneBlendOperation alphaOp);
		void _bindGLBuffer(GLenum target, GLuint buffer);
	};
}

#endif
